#include "Server.h"
#include "handler_code.h"
#include <alibabacloud/oss/OssClient.h>
#include <boost/asio.hpp>
#include <curl/curl.h>
#include <dlfcn.h>
#include <filesystem>
#include <iostream>
#include <json.h>
#include <map>
// #include<global_def.h>
// 定义

using namespace AlibabaCloud::OSS;
namespace fs = filesystem;
vector<thread> threads;
string local_path = "/home";
deque<s_info> dq_info;
mutex mtx; // 用来锁dq_info
deque<s_info> dq_process;
mutex process_mtx;
condition_variable cv;
map<string, sce_info> map_all;

void query(string camera_id, string& sce_name, string& pro_name);
int find_video(string& sce_name, string& pro_name, string& ip, string& filename);
void readStrJson(string in, string& sce_name, string& pro_name); // 从字符串中读取JSON
typedef VideoHandlerErrorCode (*video_handler_func)(const char*, const char*, const char*);

void videoHandler()
{
    void* handle = dlopen("./libvideohandler_with_db.so", RTLD_LAZY);
    if (!handle) {
        std::cerr << "Cannot open library: " << dlerror() << std::endl;
        return;
    }

    // const char* scene, const char* item, const char* video_path, const char* oss_path
    typedef void (*video_handler_func)(const char*, const char*, const char*, const char*);
    dlerror(); // Clear any existing error

    auto video_handler_pipeline = (video_handler_func)dlsym(
        handle, "video_handler_pipeline");
    const char* dlsym_error = dlerror();
    if (dlsym_error) {
        std::cerr << "Cannot load symbol 'video_handler_pipeline': "
                  << dlsym_error << std::endl;
        dlclose(handle);
        return;
    }

    const char* scene = "aiit";
    const char* item = "tour";
    const char* video_path = "./demo.mp4";
    const char* oss_path = "exampledir/test.mp4";
    video_handler_pipeline(scene, item, video_path, oss_path);

    dlclose(handle);
}

size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
    ((string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}
void consumer(int id, video_handler_func func)
{
    while (true) {
        unique_lock<mutex> lock(process_mtx);
        cv.wait(lock, [] { return !dq_process.empty(); });
        s_info temp_info = dq_process.front();
        dq_process.pop_front();
        cout << "dp_process.size=" << dq_process.size();
        lock.unlock();
        std::cout << "Consumer " << id << temp_info.sce_name << temp_info.pro_name << std::endl;
        string video_path = local_path + "/" + temp_info.sce_name + "/" + temp_info.pro_name + "/" + temp_info.ip + "/" + temp_info.filename;
        auto result = func(temp_info.sce_name.c_str(), temp_info.pro_name.c_str(), video_path.c_str()); // 算法调用
        sleep(1);
    }
}

int main()
{
    std::cout << "start video process.." << std::endl;
    int a = 1;
    cout << a << endl;

    try {
        // 算法初始化
        void* handle = dlopen("./libvideohandler_with_db.so", RTLD_LAZY);
        if (!handle) {
            std::cerr << "Cannot open library: " << dlerror() << std::endl;
            return 1;
        }

        // Clear any existing error
        dlerror();

        auto video_handler_pipeline = (video_handler_func)dlsym(handle, "video_handler_pipeline");
        const char* dlsym_error = dlerror();
        if (dlsym_error) {
            std::cerr << "Cannot load symbol 'video_handler_pipeline': " << dlsym_error << std::endl;
            dlclose(handle);
            return 1;
        }
        vector<thread> consumers;
        for (int i = 0; i < 6; ++i) {
            consumers.emplace_back(consumer, i, video_handler_pipeline);
        }

        // tcp服务器开始
        boost::asio::io_context ioc;
        Server s(ioc, 6667);
        thread t([&ioc]() { ioc.run(); });
        threads.push_back(move(t));

        // ioc.run();
        while (1) {
            if (dq_info.size() != 0) {
                std::cout << "dq_size=" << dq_info.size() << std::endl;
                s_info temp_info;
                mtx.lock();
                temp_info = dq_info.front();
                dq_info.pop_front();
                mtx.unlock();
                if (temp_info.count > 5)
                    continue;
                if (find_video(temp_info.sce_name, temp_info.pro_name, temp_info.ip, temp_info.filename)) {
                    string video_path = local_path + "/" + temp_info.sce_name + "/" + temp_info.pro_name + "/" + temp_info.ip + "/" + temp_info.filename;
                    cout << "find video success=" << video_path << endl;
                    lock_guard<mutex> lock(process_mtx);
                    cv.notify_all();
                    dq_process.push_back(temp_info);

                } else {
                    cout << "find video " << temp_info.sce_name << temp_info.ip << temp_info.filename << " wait for video 1s" << endl;
                    mtx.lock();
                    dq_info.push_back(temp_info);
                    mtx.unlock();
                    sleep(1);
                }
            } else {
                cout << "wait for tcp info 5s" << endl;
                sleep(5);
            }
        }
        for (auto& t : consumers) {
            t.join();
        }
        for (auto& t : threads) {
            t.join();
        }

        /* 释放网络等资源。*/
        ShutdownSdk();
    } catch (const exception& e) {
        cout << e.what() << endl;
    }
    return 0;
}
int find_video(string& sce_name, string& pro_name, string& ip, string& filename)
{
    string full_path = local_path + "/" + sce_name + "/" + pro_name + "/" + ip;
    bool flag = false;
    if (fs::exists(full_path)) {
        for (const auto& entry : fs::directory_iterator(full_path)) {
            if (entry.path().filename() == filename) {
                flag = true;
                break;
            }
        }
    }
    if (flag)
        return true;
    else
        return false;
}
void query(string camera_id, string& sce_name, string& pro_name)
{
    CURL* curl;
    CURLcode res;
    string readBuffer;
    curl = curl_easy_init();
    if (curl) {
        string url = "http://121.43.115.240:8030/openapi/findPlayProjectInfo?deviceNo=";
        string url_full = url + camera_id;
        curl_easy_setopt(curl, CURLOPT_URL, url_full.c_str());
        // curl_easy_setopt(curl, CURLOPT_HEADER, 1);

        struct curl_slist* headers = NULL;
        curl_slist_append(headers, "Content-Type:application/json");
        // curl_slist_append(headers,"Accept:application/json");
        // curl_easy_setopt(curl, CURLOPT_HEADER, 1);
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << endl;
        }
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }
    cout << readBuffer << endl;
    string out;
    string in(readBuffer);
    readStrJson(in, sce_name, pro_name);
    // 返回错误 {"success":false,"code":"80000","message":"设备不存在","tag":"system-center","data":null}
    // 返回正确 {"success":true,"code":"0","message":null,"tag":null,"data":{"id":52,"playProjectNo":"P1270746920616202240","playProjectName":null}}
    // readBuffer
}

void readStrJson(string in, string& sce_name, string& pro_name)
{
    Json::Reader reader;
    Json::Value value;
    if (reader.parse(in, value)) {
        string code = value["code"].asString();
        // cout<<"code="<<code<<endl;
        Json::Value arry = value["data"];

        int id = arry["id"].asInt();
        string pro_no = arry["playProjectNo"].asString();
        string sce_no = arry["scenicAreaNo"].asString();

        cout << "id=" << id << "pro_no=" << pro_no << "pro_name=" << sce_no << endl;

        string message = value["message"].asString();
        // cout<<"message="<<message<<endl;
        bool success = value["success"].asBool();
        // cout<<"success="<<success<<endl;
        string tag = value["tag"].asString();
        // cout<<"tag="<<tag<<endl;
        sce_name = sce_no;
        pro_name = pro_no;
    } else
        cout << "convert fail" << endl;
    return;
}
